package com.fyz.aaronfjava.util;

import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Author：AaronF
 * @Date：2024/1/26 17:04
 * @description 雪花算法id生成工具类
 * 测试数据
 * 5线程 有锁 自旋 100组 每组100万 一组平均 244毫秒
 * 50线程 有锁 自旋 100组 每组100万 一组平均 244毫秒
 * 5线程 无锁 自旋 100组 每组100万 一组平均 14毫秒
 * 50线程 无锁 自旋 100组 每组100万 一组平均 18毫秒
 */
public class SnowFlakeUtil {

    /**
     * 机器id占用位数
     */
    private static final int MACHINE_ID_BIT = 5;
    /**
     * 服务id占用位数
     */
    private static final int SERVICE_ID_BIT = 5;
    /**
     * 序号占用位数
     * 最大可表示2的12次方个数（4096），从0开始计算，最大值为4095
     */
    private static final int SEQUENCE_BIT = 12;
    /**
     * 序号最大值
     */
    private static final int MAX_SEQUENCE = 4095;
    /**
     * 机器id偏移量
     */
    private static final int MACHINE_ID_SHIFT = SEQUENCE_BIT;
    /**
     * 服务id偏移量
     */
    private static final int SERVICE_ID_SHIFT = MACHINE_ID_SHIFT + MACHINE_ID_BIT;
    /**
     * 时间戳偏移量
     */
    private static final int TIMESTAMP_SHIFT = SERVICE_ID_SHIFT + SERVICE_ID_BIT;
    /**
     * 锁对象
     */
    private static final Object lock = new Object();
    /**
     * 上次生成id的时间
     */
    private volatile static long lastTime = -1L;
    /**
     * 用于获取序号
     */
    private volatile static long sequence = 0L;
    /**
     * 序号原子自增
     */
    private static final AtomicLong sequenceNoLock = new AtomicLong();


    /**
     * 生成id
     *
     * @return 雪花算法生成id
     */
    public static long nextId() {
        return nextId(1L,1L);
    }

    /**
     * 生成id
     *
     * @param serviceId 服务id
     * @param machineId 机器id
     * @return 雪花算法生成id
     */
    public static long nextId(long serviceId, long machineId){
        synchronized (lock) {
            // 当前时间戳
            long currentTime = System.currentTimeMillis();
            // 判断是否达到最大序号
            if (sequence > MAX_SEQUENCE) {
                // 自旋到下一毫秒
                while (currentTime <= lastTime) {
                    currentTime = System.currentTimeMillis();
                }
                // 通过睡眠过渡到下一毫秒
//                try {
//                    Thread.sleep(1);
//                    currentTime = System.currentTimeMillis();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
            }
            if (currentTime > lastTime) {
                // 重置序号
                sequence = 0L;
            }
            // 记录上次操作时间
            lastTime = currentTime;
            return  // 时间戳部分
                    (currentTime << TIMESTAMP_SHIFT)
                    // 机器id部分
                    | (machineId << MACHINE_ID_SHIFT)
                    // 服务id部分
                    | (serviceId << SERVICE_ID_SHIFT)
                    // 序列号部分
                    | sequence ++;
       }
    }

    /**
     * 无锁生成id 理论上只有一毫秒生成超过4096个id才会重复
     *
     * @return 雪花算法生成id
     */
    public static long nextIdNoLock() {
        return nextIdNoLock(1L,1L);
    }

    /**
     * 无锁生成id 理论上只有一毫秒生成超过4096个id才会重复
     *
     * @param serviceId 服务id
     * @param machineId 机器id
     * @return 雪花算法生成id
     */
    public static long nextIdNoLock(long serviceId, long machineId) {
        return  // 时间戳部分
                (System.currentTimeMillis() << TIMESTAMP_SHIFT)
                        // 机器id部分
                        | (machineId << MACHINE_ID_SHIFT)
                        // 服务id部分
                        | (serviceId << SERVICE_ID_SHIFT)
                        // 序列号部分
                        | (sequenceNoLock.getAndIncrement() % (MAX_SEQUENCE + 1));
    }

    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis() << TIMESTAMP_SHIFT);
        System.out.println(nextIdNoLock());
    }
    /**
     * 从ID中获取时间
     * @param id 由此类生成的ID
     * @return
     */
    public static Date getTimeBySnowFlakeId(long id) {
        return new Date(id >>> TIMESTAMP_SHIFT);
    }



}
